package com.spring.flowable.convert;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.spring.flowable.domain.ExclusiveBranch;
import com.spring.flowable.domain.Inout;
import com.spring.flowable.domain.ProcessNode;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;

import java.util.*;

@SuppressWarnings("ALL")
@Slf4j
public class BpmnConvert {

    public static BpmnModel toBpmn(ProcessNode node) {
        // 一.准备工作
        BpmnModel bpmnModel = new BpmnModel();
        // 相当于图纸
        Process process = new Process();
        bpmnModel.addProcess(process);
        process.setId("Process_" + IdUtil.getSnowflakeNextIdStr());
        process.setExecutable(true);
        // 二.开始结束节点
        // 新建开始节点
        StartEvent startEvent = new StartEvent();
        startEvent.setId("_start");
        // 绘制到图纸
        process.addFlowElement(startEvent);
        // 新建结束节点
        EndEvent endEvent = new EndEvent();
        endEvent.setId("_end");// 绘制到图纸
        process.addFlowElement(endEvent);
        // 三.递归绘制节点
        drawNode(process, node, "_start", "_end", null);
        // 四.自动布局
        new BpmnAutoLayout(bpmnModel).execute();

        // 五.转xml
        BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
        byte[] convertToXML = bpmnXMLConverter.convertToXML(bpmnModel);
        String xml = new String(convertToXML);
        xml = xml.replaceAll("&lt;", "<").replaceAll("&gt;", ">");
        log.info("生成的xml:{}", xml);
        return bpmnModel;
    }

    /**
     * 绘制节点
     *
     * @param process       bpmn process 图纸
     * @param node          json的节点
     * @param preId         上一节点id
     * @param endId         结束节点
     * @param preExpression 上一节点表达式
     */
    public static void drawNode(Process process, ProcessNode node, String preId, String endId, String preExpression) {
        // 根据type绘制不同种类的节点
        Inout inout = drawNodeByType(process, node);
        // 绘制前一根线
        process.addFlowElement(createSequenceFlow(preId, inout.getIn(), preExpression));
        if (node.getNext() == null) {
            // 没有下一步, 绘制指向结束的线
            process.addFlowElement(createSequenceFlow(inout.getOut(), endId, null));
        } else {
            // 有下一步, 递归绘制下一个节点
            drawNode(process, node.getNext(), inout.getOut(), endId, null);
        }
    }

    /**
     * 绘制不同种类节点
     *
     * @param process
     * @param node
     * @return
     */
    private static Inout drawNodeByType(Process process, ProcessNode node) {
        if (node.getType().equals("AUDIT_NODE")) {
            return drawAuditNode(process, node);
        } else if (node.getType().equals("BRANCH_NODE")) {
            return drawExclusiveNode(process, node);
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 绘制审核节点
     *
     * @param process
     * @param node
     * @return
     */
    private static Inout drawAuditNode(Process process, ProcessNode node) {
        // 绘制节点
        String id = "Node_" + IdUtil.getSnowflakeNextIdStr();
        UserTask userTask = new UserTask();
        userTask.setId(id);
        userTask.setName(node.getName());
        // 设置多实例
        userTask.setAssignee("${user}");
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
        multiInstanceLoopCharacteristics.setSequential(node.getAssignee().getSequential());
        multiInstanceLoopCharacteristics.setElementVariable("user");
        // 完成条件
        multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfInstances == nrOfCompletedInstances}");
        multiInstanceLoopCharacteristics.setInputDataItem("${users}");
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        // 保存json节点配置到扩展属性
        Map<String, Object> extensions = new HashMap<>();
        extensions.put("node", node);

        addExtensionProperty(userTask, extensions, null);
        process.addFlowElement(userTask);
        return new Inout(id, id);
    }

    /**
     * 绘制分支节点
     *
     * @param process
     * @param node
     * @return
     */
    private static Inout drawExclusiveNode(Process process, ProcessNode node) {
        // 开始网关
        String startId = "Exclusive_" + IdUtil.getSnowflakeNextIdStr();
        ExclusiveGateway startGateway = new ExclusiveGateway();
        startGateway.setId(startId);
        process.addFlowElement(startGateway);
        // 结束网关
        String endId = "Exclusive_" + IdUtil.getSnowflakeNextIdStr();
        ExclusiveGateway endGateway = new ExclusiveGateway();
        endGateway.setId(endId);
        process.addFlowElement(endGateway);
        // 绘制分支
        List<ExclusiveBranch> branches = node.getExclusive();
        for (ExclusiveBranch branch : branches) {
            String expression = branch.getCondition();
            if (branch.getProcess() == null) {
                // 没有子流程，直接绘制结束线
                process.addFlowElement(createSequenceFlow(startId, endId, expression));
            } else {
                // 有子流程，递归绘制子流程
                drawNode(process, branch.getProcess(), startId, endId, expression);
            }
        }
        // int和out不一样
        return new Inout(startId, endId);
    }

    /**
     * 创建连线
     *
     * @param from
     * @param to
     * @return
     */
    public static SequenceFlow createSequenceFlow(String from, String to, String conditionExpression) {
        SequenceFlow flow = new SequenceFlow();
        flow.setId(from + "-" + to);
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        if (conditionExpression != null) {
            flow.setConditionExpression(conditionExpression);
        }
        return flow;
    }

    /**
     * 扩展->添加属性:
     * @param map
     * @return
     */
    public static void addExtensionProperty(BaseElement baseElement, Map<String, Object> map, SimplePropertyPreFilter filter) {
        List<ExtensionElement> properties = getOrCreateExtensionProperties(baseElement);
        for (String name : map.keySet()) {
            Object value = map.get(name);

            ExtensionElement childElement = new ExtensionElement();
            childElement.setName("activiti:property");
            List<ExtensionAttribute> attributeList = new ArrayList<>();
            ExtensionAttribute attributeName = new ExtensionAttribute();
            attributeName.setName("name");
            attributeName.setValue(name);
            ExtensionAttribute attributeValue = new ExtensionAttribute();
            attributeValue.setName("value");
            attributeValue.setValue(JSON.toJSONString(value, filter));
            attributeList.add(attributeName);
            attributeList.add(attributeValue);

            Map<String, List<ExtensionAttribute>> attributes = new HashMap<>();
            attributes.put("attributes", attributeList);
            childElement.setAttributes(attributes);
            properties.add(childElement);
        }
    }

    /**
     * 获取properties扩展，没有则创建
     * @return
     */
    public static List<ExtensionElement> getOrCreateExtensionProperties(BaseElement baseElement) {
        List<ExtensionElement> extensionElements = getOrCreateExtensionElements(baseElement);
        Optional<ExtensionElement> first = extensionElements.stream().filter(v -> v.getName().equals("activiti:properties")).findFirst();
        if (!first.isPresent()) {
            ExtensionElement element = new ExtensionElement();
            element.setName("activiti:properties");
            extensionElements.add(element);
            Map<String, List<ExtensionElement>> childElements = new HashMap<>();
            List<ExtensionElement> propertyElementList = new ArrayList<>();
            childElements.put("property",propertyElementList);
            element.setChildElements(childElements);
            return propertyElementList;
        }
        return first.get().getChildElements().get("property");
    }

    /**
     * 获取extensionElements扩展，没有则创建
     * @return
     */
    public static List<ExtensionElement> getOrCreateExtensionElements(BaseElement baseElement) {
        if (baseElement.getExtensionElements()==null) {
            baseElement.setExtensionElements(new HashMap<>());
        }
        Map<String, List<ExtensionElement>> extensionElementsMap = baseElement.getExtensionElements();
        if (!extensionElementsMap.containsKey("extensionElements")) {
            extensionElementsMap.put("extensionElements", new ArrayList<>());
        }
        return extensionElementsMap.get("extensionElements");
    }

}
